package com.zhangfb95.crosschain.server.controller;

import com.zhangfb95.crosschain.infra.model.Result;
import com.zhangfb95.crosschain.protocol.rpc.RequestHeaderConstants;
import com.zhangfb95.crosschain.server.infra.data.converter.ChainAccountConverter;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchainaccount.RpcChainAccountAddPropertiesReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchainaccount.RpcChainAccountAddReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchainaccount.RpcChainAccountListReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchainaccount.RpcChainAccountRemovePropertiesReq;
import com.zhangfb95.crosschain.server.infra.model.controller.request.rpcchainaccount.RpcChainAccountRemoveReq;
import com.zhangfb95.crosschain.server.infra.model.controller.response.rpcchainaccount.RpcChainAccountListRes;
import com.zhangfb95.crosschain.server.infra.model.mapper.entity.ChainAccount;
import com.zhangfb95.crosschain.server.infra.model.mapper.entity.ChainAccountProperty;
import com.zhangfb95.crosschain.server.service.ChainAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zhangfubing
 * @since 2023/12/22
 */
@RequestMapping("/rpc/chain-account")
@RestController
public class RpcChainAccountController {

    @Autowired
    private ChainAccountService chainAccountService;

    @PostMapping("/add")
    public Result<Void> add(@RequestHeader(RequestHeaderConstants.USER_ID) Long userId,
                            @RequestBody @Validated RpcChainAccountAddReq req) {
        ChainAccount chainAccount = ChainAccountConverter.INSTANCE.convert(req).setUserId(userId);
        chainAccountService.add(chainAccount, req.getProperties());
        return Result.ok();
    }

    @PostMapping("/remove")
    public Result<Void> remove(@RequestHeader(RequestHeaderConstants.USER_ID) Long userId,
                               @RequestBody @Validated RpcChainAccountRemoveReq req) {
        chainAccountService.remove(userId, req.getChainPath(), req.getChainIdentity());
        return Result.ok();
    }

    @PostMapping("/list")
    public Result<RpcChainAccountListRes> list(@RequestHeader(RequestHeaderConstants.USER_ID) Long userId,
                                               @RequestBody @Validated RpcChainAccountListReq req) {
        RpcChainAccountListRes res = new RpcChainAccountListRes();
        res.setList(new ArrayList<>());

        // 查询链账户列表
        List<ChainAccount> chainAccounts = chainAccountService.listChainAccount(userId, req.getChainPath());
        chainAccounts.sort((o1, o2) -> {
            int chainPathCompareResult = o1.getChainPath().compareTo(o2.getChainPath());
            if (chainPathCompareResult == 0) {
                return o1.getChainIdentity().compareTo(o2.getChainIdentity());
            }
            return chainPathCompareResult;
        });
        // 查询链账户属性列表
        List<Long> chainAccountIds = chainAccounts.stream().map(ChainAccount::getId).collect(Collectors.toList());
        List<ChainAccountProperty> chainAccountProperties =
                chainAccountService.listChainAccountProperties(chainAccountIds);
        // 分组链账户属性列表
        Map<Long, List<ChainAccountProperty>> chainAccountPropertiesMap = chainAccountProperties.stream()
                .collect(Collectors.groupingBy(ChainAccountProperty::getChainAccountId));
        // 拼装返回结果
        chainAccounts.forEach(chainAccount -> {
            RpcChainAccountListRes.Item resItem = ChainAccountConverter.INSTANCE.convert(chainAccount);
            List<ChainAccountProperty> curChainAccountProperties = chainAccountPropertiesMap.get(chainAccount.getId());
            if (!CollectionUtils.isEmpty(curChainAccountProperties)) {
                Map<String, String> properties = curChainAccountProperties.stream()
                        .collect(Collectors.toMap(
                                ChainAccountProperty::getPropertyKey,
                                ChainAccountProperty::getPropertyValue
                        ));
                resItem.setProperties(properties);
            }
            res.getList().add(resItem);
        });

        return Result.ok(res);
    }

    @PostMapping("/addProperties")
    public Result<Void> addProperties(@RequestHeader(RequestHeaderConstants.USER_ID) Long userId,
                                      @RequestBody @Validated RpcChainAccountAddPropertiesReq req) {
        chainAccountService.addProperties(userId, req.getChainPath(), req.getChainIdentity(), req.getProperties());
        return Result.ok();
    }

    @PostMapping("/removeProperties")
    public Result<Void> removeProperties(@RequestHeader(RequestHeaderConstants.USER_ID) Long userId,
                                         @RequestBody @Validated RpcChainAccountRemovePropertiesReq req) {
        chainAccountService.removeProperties(userId, req.getChainPath(), req.getChainIdentity(), req.getPropertyKeys());
        return Result.ok();
    }
}
